package ferp.core.mode;

import ferp.core.card.Card;
import ferp.core.game.Bid;
import ferp.core.game.Game;
import ferp.core.game.Input;
import ferp.core.game.Settings;
import ferp.core.player.Player;
import ferp.core.player.Profile;
import ferp.core.state.State;
import ferp.core.tutorial.Scenario;

/**
 * User: olegs
 * Date: 19/05/11 15:11
 */
public class Bidding extends Mode
{
  @Override
  public int initialize(Game.Listener listener, Profile profile, Game g, Settings settings) throws Game.Error
  {
    // common mode initialization
    int r = super.initialize(listener, profile, g, settings);

    // fire bidding solve event
    listener.onBiddingStart(g);

    return r;
  }

  @Override
  public void mark(Game.Listener listener, Game g)
  {
    Bid bid = g.scenario().bid(g.rounds, g.human().id());

    switch (bid.type)
    {
      case PLAY:
        listener.markBidPlay();
        break;

      case MISERE:
        listener.markBidMisere();
        break;

      case PASS:
        listener.markBidPass();
        break;
    }
  }

  @Override
  protected Scenario.Note getSetOverNote(Game g, Settings settings)
  {
    return g.scenario().getBiddingSetOverNote();
  }

  @Override
  protected int options(Game.Listener listener, Game g, Settings settings, Input input) throws Game.Error
  {
    Player p = g.current();
    Bid bid = g.getBid(p.id()).current;

    int r = p.setBiddingInputOptions(g, g.options);

    if (bid != null)
    {
      // if this player already passed do not turn on any option
      if (bid.isPass())
        return State.Result.SCHEDULE;

      // if player bid misere in previous round now he can bid pass only (i.e. some other player bids at least 9)
      if (bid.isMisere())
      {
        g.options.set(Input.Options.PASS);
        return r;
      }
    }

    // can "pass" and "play"
    g.options.set(Input.Options.PASS | Input.Options.PLAY);

    // can declare misere only on first bid and when no other player bid misere or 9+ game
    if (bid == null && (g.bid == null || (!g.bid.isMisere() && g.bid.tricks < 9)))
      g.options.set(Input.Options.MISERE);

    // set minimum bid to highest + 1 or highest if bidding after passers
    if (g.bid == null)
    {
      // first bid (6 of spades)
      g.options.bid.type = Bid.Type.PLAY;
      g.options.bid.trump = Card.Suit.SPADES;
      g.options.bid.tricks = g.getStartingTricks(settings);
      g.options.bid.talon = true;
    }
    else
    {
      // if I am bidding after passer and no one bid misere - I can tell "here"
      if (g.getBid(g.previous(p).id()).current.isPass() && !g.bid.isMisere() && !g.bid.isStalingrad(settings))
        g.options.bid.assign(g.bid);
      else
      {
        Bid next = g.bid.next(g);

        // if the maximum bid reached - enable only "pass" option
        if (next == null)
          g.options.clear(Input.Options.PLAY);
        else
          g.options.bid.assign(next);
      }
    }

    return r;
  }

  @Override
  protected Scenario.Note getOptionsNote(Game g, Settings settings)
  {
    return g.scenario().getBiddingOptionsNote(g.rounds, g.player.current);
  }

  @Override
  protected boolean validate(Game.Listener listener, Game g, Settings settings, Input input)
  {
    boolean valid = input.bid.equals(g.scenario().bid(g.rounds, g.human().id()));

    if (!valid)
      listener.onInvalidBid();

    return valid;
  }

  @Override
  protected Act act(Game.Listener listener, Game g, Settings settings, Input input) throws Game.Error
  {
    Player p = g.current();

    // bid only if not passed in the previous round and not all other players said pass yet
    if (g.getBid(p.id()).current == null || !g.getBid(p.id()).current.isPass())
    {
      p.bid(listener, g, settings, input);

      if (g.getBid(p.id()).current.isPass())
        ++g.passers;
      else
        if (g.getBid(p.id()).current.isPlay() || g.getBid(p.id()).current.isMisere())
        {
          // update the highest bid
          g.bid = g.getBid(p.id()).current;
          // this player becomes the last declarer
          g.player.declarer = p.id();
        }

      // fire bid event
      listener.onPlayerBid(p, g.getBid(p.id()));
    }

    return Act.NEXT;
  }

  @Override
  protected boolean isRoundOver(Game g)
  {
    // to be able to leave the round when the set is actually over but hand < 2
    return super.isRoundOver(g) || isSetOver(g);
  }

  @Override
  protected boolean isSetOver(Game g)
  {
    // either 2 or 3 players must "pass"
    return g.passers == 3 || (g.passers == 2 && g.bid != null);
  }

  @Override
  protected void done(Game.Listener listener, Game g, Settings settings)
  {
    switch (g.passers)
    {
      case 2:
        // one player is declaring there will be a play set (tricking or misere)
        // fire show talon cards events
        listener.onTalonShow(g, settings);
        // switching to the dropping mode
        g.set(Mode.dropping);

        break;

      case 3:
        // everyone has passed - switching to the passing mode
        g.set(Mode.passing);

        break;

      default:
        assert false: "invalid number of passers: " + g.passers;
    }
  }
}
